<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="FcCzL" id="FcCzL"><span data-lake-id="u8ad8fab7" id="u8ad8fab7">典型回答</span></h1>
  <p data-lake-id="uea413837" id="uea413837"><br></p>
  <p data-lake-id="u1bbdfa28" id="u1bbdfa28"><span data-lake-id="ud19fa71f" id="ud19fa71f">Leaf是美团的分布式ID框架，他有2种生成ID的模式，分别是Snowflake和Segment。</span></p>
  <p data-lake-id="u447d98b7" id="u447d98b7"><span data-lake-id="ubb080ffe" id="ubb080ffe">​</span><br></p>
  <p data-lake-id="u9c979cab" id="u9c979cab"><strong><span data-lake-id="u3115992a" id="u3115992a">在Segment（号段）模式中</span></strong><span data-lake-id="uff745502" id="uff745502">，他的意思就是每次去数据库中取ID的时候取出来一批，并放在缓存中，然后下一次生成新ID的时候就从缓存中取。这一批用完了再去数据库中拿新的。</span></p>
  <p data-lake-id="ub407b943" id="ub407b943"><span data-lake-id="u65f29fb8" id="u65f29fb8">​</span><br></p>
  <p data-lake-id="u0c59f8a3" id="u0c59f8a3"><span data-lake-id="uc84a0be6" id="uc84a0be6">而为了防止多个实例之间发生冲突，需要采用号段的方式，即给每个客户端发放的时候按号段分开，如客户端A取的号段是1-1000，客户端B取的是1001-2000，客户端C取的是2001-3000。当客户端A用完之后，再来取的时候取到的是3001-4000。</span></p>
  <p data-lake-id="u1d48c58f" id="u1d48c58f"><span data-lake-id="ua9cf91c4" id="ua9cf91c4">​</span><br></p>
  <p data-lake-id="uada7571a" id="uada7571a"><span data-lake-id="u4b10fefc" id="u4b10fefc">号段模式的好处是在同一个客户端中，生成的ID是顺序递增的。并且不需要频繁的访问数据库，也能提升获取ID的性能。</span></p>
  <p data-lake-id="ufd022d05" id="ufd022d05"><span data-lake-id="ubabfe9ad" id="ubabfe9ad">​</span><br></p>
  <p data-lake-id="u18535c93" id="u18535c93"><span data-lake-id="u50519c36" id="u50519c36">​</span><br></p>
  <p data-lake-id="u9be3023d" id="u9be3023d"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1680875334961-33fea7cd-a539-4fa2-b892-925928f4d53a.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_21%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <h3 data-lake-id="nAPqa" id="nAPqa" style="text-align: left"><br></h3>
  <p data-lake-id="ud8f9442d" id="ud8f9442d"><span data-lake-id="u310d9297" id="u310d9297">这种模式的优点是，虽然依赖数据库，但是因为有号段缓存，所以在数据库宕机后的一段时间内也能保证可用性，并且这种模式不依赖时钟，所以不存在时钟回拨的问题。</span></p>
  <p data-lake-id="u4d05d5c6" id="u4d05d5c6"><span data-lake-id="u9e5146b0" id="u9e5146b0">​</span><br></p>
  <p data-lake-id="u1dc169e1" id="u1dc169e1"><span data-lake-id="u02185ddf" id="u02185ddf">缺点也比较明显，首先是如果多个缓存中刚好用完了号段，同时去请求数据库获取新的号段时可能会导致并发争抢影响性能，另外，DB如果宕机时间过长，缓存中号段耗尽也会有可用性问题。</span></p>
  <p data-lake-id="u751b5d40" id="u751b5d40"><span data-lake-id="u6a4fd9f3" id="u6a4fd9f3">​</span><br></p>
  <p data-lake-id="ue5f53353" id="ue5f53353"><span data-lake-id="ucfb06b0e" id="ucfb06b0e">为了解决多个号段用完之后取新的号段冲突，Leaf还引入了双buff，当号段消费到某个阈值时就异步的把下一个号段加载到内存中，而不需要定好耗尽才去更新，这样可以避免取号段的时候导致没有号码分配影响可用性及性能。</span></p>
  <p data-lake-id="u662e9c66" id="u662e9c66"><span data-lake-id="u88bbe3d9" id="u88bbe3d9">​</span><br></p>
  <p data-lake-id="uac678516" id="uac678516"><span data-lake-id="u9003207d" id="u9003207d">​</span><br></p>
  <p data-lake-id="ube809515" id="ube809515"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1680875579179-04f1ed6e-0af9-4f48-a3d4-aa91432be3c1.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_22%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uf0c8c0fe" id="uf0c8c0fe"><br></p>
  <p data-lake-id="ub787cd3b" id="ub787cd3b"><strong><span data-lake-id="u3f5679b5" id="u3f5679b5">在Snowflake模式中</span></strong><span data-lake-id="uda429c10" id="uda429c10">，当然是基于基于Twitter的Snowflake算法实现的了。但是主要是针对Snowflake中存在的一些问题做了很多优化。</span></p>
  <p data-lake-id="uc940dd51" id="uc940dd51"><span data-lake-id="u4c85424b" id="u4c85424b">​</span><br></p>
  <p data-lake-id="u9d23f1f5" id="u9d23f1f5"><span data-lake-id="uccb8e699" id="uccb8e699">相对于Snowflake，Leaf 在以下几个方面进行了优化：</span></p>
  <p data-lake-id="u78c11867" id="u78c11867"><span data-lake-id="u3b7f985c" id="u3b7f985c">​</span><br></p>
  <ol list="ub8dc767b">
   <li fid="u255a151c" data-lake-id="ud6df1b56" id="ud6df1b56"><strong><span data-lake-id="u32f9fa99" id="u32f9fa99">数据中心ID和机器ID的配置方式</span></strong><span data-lake-id="u38445eb5" id="u38445eb5">：Snowflake需要在代码中硬编码数据中心ID和机器ID，而Leaf通过配置文件的方式进行配置，可以动态配置数据中心ID和机器ID，降低了配置的难度。</span></li>
   <li fid="u255a151c" data-lake-id="u39d58d39" id="u39d58d39"><span data-lake-id="u6b63e85a" id="u6b63e85a">引入区间概念：Leaf引入了区间的概念，每次从zookeeper获取一段ID的数量（比如1万个），之后在这个区间内产生ID，避免了每次获取ID都要去zookeeper中获取，</span><strong><span data-lake-id="u33f79723" id="u33f79723">减轻了对zookeeper的压力，并且也可以减少对ZK的依赖，并且也能提升生成ID的效率。</span></strong></li>
   <li fid="u255a151c" data-lake-id="uca39365c" id="uca39365c"><span data-lake-id="ub3328586" id="ub3328586">自适应调整：Leaf支持自适应调整ID生成器的参数，比如每个区间的ID数量、ID生成器的工作线程数量等，可以根据实际情况进行动态调整，提高了系统的性能和灵活性。</span></li>
   <li fid="u255a151c" data-lake-id="u24635551" id="u24635551"><span data-lake-id="ub3e49cc4" id="ub3e49cc4">支持多种语言：Leaf不仅提供了Java版本的ID生成器，还提供了Python和Go语言的版本，可以满足不同语言的开发需求。</span></li>
   <li fid="u255a151c" data-lake-id="uca37321b" id="uca37321b"><strong><span data-lake-id="u4fbb9928" id="u4fbb9928">时钟回拨解决</span></strong><span data-lake-id="u67796f24" id="u67796f24">：每个 Leaf 运行时定时向 zk 上报时间戳。每次 Leaf 服务启动时，先校验本机时间与上次发 ID 的时间，再校验与 zk 上所有节点的平均时间戳。如果任何一个阶段有异常，那么就启动失败报警。</span></li>
  </ol>
  <p data-lake-id="uad989923" id="uad989923"><span data-lake-id="u49aea31f" id="u49aea31f">​</span><br></p>
  <p data-lake-id="uc8b95196" id="uc8b95196"><span data-lake-id="ucd6d99b8" id="ucd6d99b8">Leaf的分布式ID生成过程可以简述如下：</span></p>
  <ol list="u4f6d62cd">
   <li fid="ueb63ea7a" data-lake-id="ufa2b34a0" id="ufa2b34a0"><span data-lake-id="u9e7b3723" id="u9e7b3723">Leaf生成器启动时，会从配置文件中读取配置信息，包括数据中心ID、机器ID等。</span></li>
   <li fid="ueb63ea7a" data-lake-id="u5fb8d1dc" id="u5fb8d1dc"><span data-lake-id="ub250d140" id="ub250d140">Leaf生成器会向zookeeper注册自己的信息，包括IP地址、端口号等。</span></li>
   <li fid="ueb63ea7a" data-lake-id="u1f9dd0a9" id="u1f9dd0a9"><span data-lake-id="ud3b95a28" id="ud3b95a28">应用程序需要生成一个ID时，会向Leaf生成器发送一个请求。</span></li>
   <li fid="ueb63ea7a" data-lake-id="u27252e05" id="u27252e05"><span data-lake-id="u52cb5783" id="u52cb5783">Leaf生成器会从zookeeper中读取可用的区间信息，并分配一批ID。</span></li>
   <li fid="ueb63ea7a" data-lake-id="uf35955d0" id="uf35955d0"><span data-lake-id="ubbb017da" id="ubbb017da">Leaf生成器将分配的ID返回给应用程序。</span></li>
   <li fid="ueb63ea7a" data-lake-id="uea7d45f2" id="uea7d45f2"><span data-lake-id="u1ae8d9d3" id="u1ae8d9d3">应用程序可以使用返回的ID生成具体的业务ID。</span></li>
   <li fid="ueb63ea7a" data-lake-id="u1e213471" id="u1e213471"><span data-lake-id="uc246b561" id="uc246b561">当分配的ID用完后，Leaf生成器会再次向zookeeper请求新的区间。</span></li>
  </ol>
  <p data-lake-id="uc00119ae" id="uc00119ae"><span data-lake-id="u121803db" id="u121803db">​</span><br></p>
  <p data-lake-id="ubdc81f8d" id="ubdc81f8d"><span data-lake-id="ueaaf1fb1" id="ueaaf1fb1">​</span><br></p>
 </body>
</html>